package org.idea.spring.aop.version2.proxy;

import net.sf.cglib.proxy.Enhancer;
import org.idea.spring.aop.cglib.MyBis;
import org.idea.spring.aop.cglib.TargetInterceptor;
import org.idea.spring.aop.version2.bean.Advice;
import org.idea.spring.aop.version2.bean.AdviceHelper;
import org.idea.spring.aop.version2.bean.Advisor;
import org.idea.spring.aop.version2.config.AnnotationParser;
import org.idea.spring.aop.version2.interceptor.AfterMethodInterceptor;
import org.idea.spring.aop.version2.interceptor.BeforeMethodInterceptor;
import org.idea.spring.aop.version2.interceptor.IMethodInterceptorChains;
import org.idea.spring.aop.version2.service.TestService;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.DependsOn;

import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.regex.Pattern;

/**
 * @Author linhao
 * @Date created in 4:14 下午 2021/5/7
 */
@Configuration
@DependsOn("annotationParser")
public abstract class AbstractAOPProxyCreator implements BeanPostProcessor, BeanFactoryAware {

    private BeanFactory beanFactory;

    @Resource
    private CglibProxyImpl cglibProxyImpl;

    private Map<String, Advisor> advisorMap = new ConcurrentHashMap<>();

    private Map<String,List<Advisor>> advisorListMap = new ConcurrentHashMap<>();

    private List<AdviceHelper> adviceHelpers = new ArrayList<>();

    protected abstract Object createProxy(List<Advisor> advisors, Object bean);

    protected abstract List<Advisor> buildAdvisors();

    private AnnotationParser annotationParser;

    /**
     * 供子类访问
     *
     * @param beanFactory
     */
    protected void initBeanFactory(BeanFactory beanFactory) {
        this.beanFactory = beanFactory;
    }

    @Override
    public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
        this.initBeanFactory(beanFactory);
    }

    @PostConstruct
    public void doInit() {
        AnnotationParser annotationParser = beanFactory.getBean(AnnotationParser.class);
        List<Advisor> advisors = annotationParser.parse();
        for (Advisor advisor : advisors) {
            AdviceHelper adviceHelper = new AdviceHelper();
            adviceHelper.setPointCutClassPattern(Pattern.compile(advisor.getPointCut()));
            adviceHelper.setAspectNameKey(advisor.getAspectClass());
            this.advisorMap.put(advisor.getAspectClass(), advisor);
            this.adviceHelpers.add(adviceHelper);
        }
    }

    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        //将所有的advice对象中的pointcut规则抽离出来，然后逐个的bean进行匹配
        for (AdviceHelper adviceHelper : adviceHelpers) {
            boolean isMatch = adviceHelper.isMatchTarget(bean.getClass().getName());
            if (isMatch) {
                Advisor advisor = advisorMap.get(adviceHelper.getAspectNameKey());
                IMethodInterceptorChains iMethodInterceptorChains = new IMethodInterceptorChains();
                iMethodInterceptorChains.put("before",new BeforeMethodInterceptor(advisor.getAspectObj(),advisor.getAspectBeforeMethod()));
                iMethodInterceptorChains.put("after",new AfterMethodInterceptor(advisor.getAspectObj(),advisor.getAspectAfterMethod()));
                CglibTargetInterceptor cglibTargetInterceptor = new CglibTargetInterceptor(iMethodInterceptorChains);
                Object proxyObj = cglibProxyImpl.createProxy(bean.getClass(),cglibTargetInterceptor);
                return proxyObj;
            }
        }
        return bean;
    }


    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) {
        return bean;
    }

}
